用Qt框架搭建一个简易画板
需求
绘制线、椭圆、矩形框、任意凹/凸多边形、曲线
删除最近的图形实例
思路
用list保存绘制的图形实例，便于删除
对于line、rectangle、ellipse 只要保存初始位置和结束位置
对于任意凸/凹多边形可以用一个list保存点集
利用Graphics View 管理图形对象
Graphics View 是M-V框架，model指的是各种图形对象，view指的是视角
使用过程是：创建一个scene,创建line和rectangle等图形实例,再使用scene的add函数将line、rectangle实例等添加到scene中，最后通过视口view就可以看到了；同一个model可以从不同的view进行观察
创建图形对象继承于父类Shape,绘制完毕后将图形对象存储在List中，点击删除按钮时，从List中出栈一个图形对象，重绘
界面样式 

按钮描述(从左至右) 
直线按钮：点击进入绘制直线模式：按下左键设置起始点，拖动鼠标至结束点，释放左键绘制完毕 
矩形按钮：点击进入绘制矩形模式：按下左键设置左上角，拖动鼠标至结束点，释放左键绘制完毕 
椭圆按钮：点击进入绘制椭圆模式：同矩形按钮 
多边形按钮：点击进入绘制多边形模式：单击左键设置起始点，拖动鼠标至下一个转折点，继续单击鼠标添加转折点，直到单击右键结束绘制 
曲线按钮：点击进入绘制曲线模式：左键按下并拖动鼠标绘制 
删除按钮：点击进入删除模式：逆序删除绘制的图形对象
问题解决Q&A
1.问题: 在Qt中，添加类之后经常出现“无法解析的外部命令的问题” 
解决方法：http://blog.csdn.net/yz960611/article/details/50735406 
http://blog.csdn.net/zhoxier/article/details/8619688
Tuitor
1.创建主界面 
这一步主要是mainWindow.cpp、mainWindow.h、mainWindow.ui三个文件 
新建Qt Widegts Application,项目名是miniDraw,基类是QMainWindow,类名是MainWindow，头文件mainWindow.h,源文件mainWindow.cpp,勾选创建界面。 

在mainWindow.h中添加头文件 
#include<vector> 
#include<shape.h>//后面会讲到
添加成员变量 
```cpp 
private： 
Shape::drawMode mode;//保存目前的绘图模式，分为七中，在shape.h中定义 
Shape *shape;//五种不同的图形对象 
bool isDone=false;//是否绘制完毕 
QList
 * 添加成员函数
```cpp
protected:
    void paintEvent(QPaintEvent *event);//绘图事件，用update()调用
    void mousePressEvent(QMouseEvent *event);//鼠标按下
    void mouseMoveEvent(QMouseEvent *event);//鼠标移动
    void mouseReleaseEvent(QMouseEvent *event);//鼠标释放
编写界面 
仿照主界面示意图添加组件，具体使用方法查看Qt Designer,使用设计模式创建界面 
 
（基本步骤：1、创建actionLine;2、编辑图标；3、添加statusTip;4、转到槽函数；） 
2、创建各种图形对象的父类——Shape类 
（1、右键单击工程；2、添加新文件；2、C++ Class；3、Define Class） 
 
```cpp 
//shape.h
ifndef SHAPE_H
define SHAPE_H
include
include
class Shape 
{ 
public: 
enum drawMode//绘图模式 
{ 
LINE, 
RECT, 
ELLIPSE, 
POLYGON, 
FREEHAND, 
DELETE, 
DEFAULT 
}; 
Shape(); 
virtual ~Shape();//虚函数，防止内存泄漏，具体见后面 
void setStart(QPoint start) 
{ 
this->start=start; 
} 
void setEnd(QPoint end) 
{ 
this->end=end; 
} 
QPoint getStart() 
{ 
return start; 
} 
QPoint getEnd() 
{ 
return end; 
} 
void virtual paint(QPainter &painter)=0;//需要在子类中重写的函数设置为虚函数 
protected: 
QPoint start,end; 
};
endif // SHAPE_H
 ```cpp
 //shape.cpp
#include "shape.h"
Shape::Shape()
{
}
2.1创建子类line、Rectangle、Ellipse、Polygon、FreeHand——主要是重写paint函数
line 
```cpp 
void line::paint(QPainter &painter) 
{ 
painter.drawLine(start,end); 
}
  * **rectangle**
```cpp
void Rectangle::paint(QPainter &painter)
{
    painter.drawRect(start.x(),start.y(),end.x()-start.x(),end.y()-start.y());
}
ellipse
void Ellipse::paint(QPainter &painter)
{
    painter.drawEllipse(start.x(),start.y(),end.x()-start.x(),end.y()-start.y());
}
以上这些都很简单
freehand 
```cpp 
//freehand.h
ifndef FREEHAND_H
define FREEHAND_H
include"shape.h"
include
class FreeHand:public Shape 
{ 
public: 
FreeHand(); 
FreeHand(const QVector
endif // FREEHAND_H
```cpp
//构造函数和绘制函数的实现
FreeHand::FreeHand(const QVector<QPoint> &point_array)
{
    for(size_t i=0;i<point_array.size();i++)
    {
        //free_point_array[i]=point_array[i];
        free_point_array.push_back(point_array[i]);
    }
}
void FreeHand::paint(QPainter &painter)
{
    for(size_t i=0;i<free_point_array.size()-1;i++)
    {
        painter.drawLine(free_point_array[i],free_point_array[i+1]);
    }
}
polygon 
```cpp 
//构造函数和绘图函数的实现 
Polygon::Polygon(const QVector
**以上两个是比较难的，需要考虑一下**
 **3整体逻辑——对成员函数的实现**
 **3.1给按钮添加槽函数**
 （这里我是采用Qt Creator 添加的，这不是我们讲的重点）


```cpp
void MainWindow::on_actionLine_triggered()
{
    mode=Shape::drawMode::LINE;
}
void MainWindow::on_actionRectangle_triggered()
{
    mode=Shape::drawMode::RECT;
}
void MainWindow::on_actionEllipse_triggered()
{
    mode=Shape::drawMode::ELLIPSE;
}
void MainWindow::on_actionPolygon_triggered()
{
    mode=Shape::drawMode::POLYGON;
}
void MainWindow::on_actionFreehand_triggered()
{
    mode=Shape::drawMode::FREEHAND;
}
void MainWindow::on_actionDelete_triggered()
{
    mode=Shape::drawMode::DELETE;
    if(!shapeList.isEmpty())
    {
        shapeList.pop_back();//最后一个图形对象出栈
        mode=Shape::DEFAULT;//设置模式为默认模式
        update();
    }
}
逻辑就是点击不同的按钮进入不同的绘制模式 
3.2给鼠标按键添加监听
绘制直线：按下鼠标左键拖动鼠标，直到目的位置释放鼠标；
绘制矩形：同绘制直线；
绘制椭圆：同绘制矩形
绘制多边形：点击鼠标左键设置第一个锚点，再次单击设置第二个锚点，以此类推，设置完最后一个锚点后点击鼠标右键结束绘制；
绘制曲线：按住左键不放绘制曲线，释放左键结束绘制
从功能需求我们可以看出，绘制直线、矩形、椭圆实际上只需要两个点就可以确定，那么就可以按按下鼠标的位置作为起始位置，释放鼠标的位置作为结束位置
绘制多边形和绘制曲线很相似，就是往vector里面添加一系列点
因此，在绘制直线、椭圆、矩形的时候，可以在按下左键的时候就将图形对象保存进shapeList
在绘制曲线、多边形时可以在主函数里面暂存这一系列点，等绘制完毕再把图形对象保存进shapeList，这样可以提高绘制的效率
```cpp 
//鼠标按下 
void MainWindow::mousePressEvent(QMouseEvent *event) 
{ 
switch (event->button()) { 
case Qt::LeftButton ://在绘制直线、矩形、多边形、椭圆、曲线时有效，开始绘制 
switch (mode) { 
case Shape::DEFAULT: 
break; 
case Shape::DELETE: 
//do nothing 
break; 
case Shape::LINE: 
shape=new line; 
break; 
case Shape::RECT: 
shape=new Rectangle; 
break; 
case Shape::ELLIPSE: 
shape=new Ellipse; 
break; 
case Shape::FREEHAND: 
shape=new FreeHand; 
break; 
case Shape::POLYGON: 
shape=new Polygon; 
break; 
default: 
break; 
} 
break; 
case Qt::RightButton: //仅在绘制多边形时有效，结束绘制 
if(mode==Shape::POLYGON) 
{ 
isDone=true; 
if(polygon_point_array.size()>0) 
{ 
shape=new Polygon(polygon_point_array); 
shapeList.push_back(shape); 
polygon_point_array.clear(); 
update(); 
shape=NULL; 
} 
} 
break; 
default: 
break; 
} 
if(shape!=NULL){//刚开始绘制 
if(mode==Shape::POLYGON)//设置转折点 
{ 
isDone=false; 
polygon_point_array.push_back(event->pos());//添加进锚点 
freePoint=event->pos(); 
update(); 
}else if(mode==Shape::FREEHAND) 
{ 
isDone=false; 
freehand_point_array.push_back(event->pos());//添加进锚点 
} 
else { 
isDone=false; 
shape->setStart(event->pos()); 
shape->setEnd(event->pos()); 
shapeList.push_back(shape);//将图形对象入栈 
} 
} 
}
```cpp
//鼠标移动
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
    if(shape&&!isDone)
    {
        if(mode==Shape::FREEHAND)
        {
            freehand_point_array.push_back(event->pos());//添加曲线点序列
            update();
        }else
        {
            shape->setEnd(event->pos());
            freePoint=event->pos();
            update();
        }
    }
}
//鼠标释放
void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button()==Qt::LeftButton)//仅在左键释放时有效，结束绘制直线、矩形、椭圆、曲线
    {
        switch (mode) {
        case Shape::POLYGON:
            break;
        case Shape::FREEHAND:
            isDone=true;
            shape=new FreeHand(freehand_point_array);
            freehand_point_array.clear();
            shapeList.push_back(shape);
            update();
            shape=NULL;
            break;
        default:
            isDone=true;
            update();
            shape=NULL;
            break;
        }
    }
}